home *** CD-ROM | disk | FTP | other *** search
- ;; PC Scheme Common Lisp Compatibility Package
- ;;
- ;; (c) Copyright 1990 Carl W. Hoffman. All rights reserved.
- ;;
- ;; This file may be freely copied, distributed, or modified for non-commercial
- ;; use provided that this copyright notice is not removed. For further
- ;; information about other utilities for Common Lisp or Scheme, contact the
- ;; following address:
- ;;
- ;; Carl W. Hoffman, 363 Marlborough Street, Boston, MA 02115, U.S.A.
- ;; Internet: CWH@AI.MIT.EDU CompuServe: 76416,3365 Fax: 617-262-4284
-
- ;; FORMAT, WARN, ERROR
-
- ;; To do:
- ;; CERROR
- ;; FORMAT optimizer. Define FORMAT as a macro, and have #'FORMAT and
- ;; (FUNCTION FORMAT) be functions. Also, perhaps APPLYF, etc. should
- ;; convert to function form.
-
- (defun format (destination control-string &rest arguments)
- (if (null destination)
- (with-output-to-string (destination)
- (format-internal destination control-string arguments))
- (format-internal
- (if (eq destination t) (current-output-port) destination)
- control-string arguments)))
-
- (defvar *format-directives* nil)
-
- ;; This should be implemented using a resource in case we ever
- ;; invoke format recursively or in another task.
-
- (defvar format-buffer (make-string 300))
-
- (defun-clcp format-internal (output-stream control-string arguments)
- (with-input-from-string (input-stream control-string)
- (do ((index 0)
- (next-arg arguments)
- (format-conditional nil)
- (ch (read-char input-stream nil nil)
- (read-char input-stream nil nil)))
- ((null ch)
- (unless (zerop index)
- (write-string format-buffer output-stream :end index)))
- (if (not (char= ch #\~))
-
- ;; Accumulate ordinary characters in a buffer to reduce
- ;; the number of stream operations.
-
- (unless (eq format-conditional 'false)
- (setf (char format-buffer index) ch)
- (incf index))
-
- ;; For now, just flush the buffer whenever we encounter a format
- ;; directive. We could optimize further by building the entire
- ;; string inside format.
-
- (progn
- (unless (zerop index)
- (write-string format-buffer output-stream :end index)
- (setq index 0))
- (do ((numeric-arg nil)
- (atsign-flag nil)
- (colon-flag nil)
- (ch))
- (nil)
- (setq ch (read-char input-stream nil nil))
- (when (null ch)
- (error "End of format string in the middle of a directive"))
- (if (and (char<=? #\0 ch) (char<=? ch #\9))
- (let ((digit (- (char-code ch) (char-code #\0))))
- (setq numeric-arg
- (if (null numeric-arg)
- digit
- (+ (* numeric-arg 10) digit))))
- (progn
- (setq ch (char-upcase ch))
- (case ch
- (#\@
- (when atsign-flag
- (error "Multiple atsign flags in format directive"))
- (setq atsign-flag t))
- (#\:
- (when colon-flag
- (error "Multiple colon flags in format directive"))
- (setq colon-flag t))
- (#\[
- (when format-conditional
- (error "~[ seen inside ~["))
- (setq format-conditional
- (if (pop next-arg) 'false 'true))
- (return nil))
- (#\]
- (unless format-conditional
- (error "~] seen before ~["))
- (setq format-conditional nil)
- (return nil))
- (#\;
- (unless format-conditional
- (error "~; seen before ~["))
- (setq format-conditional
- (if (eq format-conditional 'false)
- 'true
- 'false))
- (return nil))
- (else
- (let ((directive
- (cdr (assoc ch *format-directives*))))
- (unless directive
- (error "Unimplemented FORMAT directive" c))
- (unless (eq format-conditional 'false)
- (setq next-arg
- ((eval directive)
- input-stream output-stream next-arg
- numeric-arg atsign-flag colon-flag))))
- (return nil))))))))))
- nil)
-
- (defun add-format-directive (char name)
- (let ((pair (assoc char *format-directives*)))
- (if pair
- (setf (cdr pair) name)
- (push (cons char name) *format-directives*))))
-
- (defmacro define-format-directive (name char &body body)
- (let ((function-name (symbol-append 'format- name)))
- `(begin
- (define (,function-name input-stream output-stream next-arg
- numeric-arg atsign-flag colon-flag)
- ,@body
- next-arg)
- (add-format-directive ,char ,function-name))))
-
- (define-format-directive ~ #\~
- (dotimes (i (or numeric-arg 1)) (write-char #\~ output-stream)))
-
- (define-format-directive % #\%
- (dotimes (i (or numeric-arg 1)) (terpri output-stream)))
-
- (define-format-directive & #\&
- (fresh-line output-stream))
-
- (define-format-directive newline #\newline
- (when atsign-flag
- (terpri output-stream))
- (unless colon-flag
- (do () (nil)
- (let ((c (read-char input-stream)))
- (unless (char= c #\space)
- (un-read-char c input-stream)
- (return nil))))))
-
- (define-format-directive s #\S
- (write (pop next-arg) :stream output-stream :escape t))
-
- (define-format-directive a #\A
- (write (pop next-arg) :stream output-stream :escape nil))
-
- (defun format-integer (arg output-stream number-format description)
- (unless (integerp arg)
- (error "The argument to ~~~A, ~S, is not an integer."
- description arg))
- (write-string (number->string arg number-format) output-stream))
-
- (define-format-directive b #\B
- (format-integer (pop next-arg) output-stream '(int (radix b s)) "B"))
-
- (define-format-directive o #\O
- (format-integer (pop next-arg) output-stream '(int (radix o s)) "O"))
-
- (define-format-directive d #\D
- (format-integer (pop next-arg) output-stream '(int (radix d s)) "D"))
-
- (define-format-directive x #\X
- (format-integer (pop next-arg) output-stream '(int (radix x s)) "X"))
-
- ;; Make this inline so that there is one less frame on the stack when
- ;; debugging. Later, we may have a debugger which can hide this frame.
-
- (defun-inline error (format-string &rest format-args)
- (scheme-error (apply (function format) nil format-string format-args)))
-
- ;; Good enough for now
-
- (defvar typespec-alist
- '((array vector? "an array")
- (character char? "a character")
- (compiled-function procedure? "a compiled function")
- (cons pair? "a cons cell")
- (double-float float? "a double-precision floating point number")
- (float float? "a floating point number")
- (integer integer? "an integer")
- (list listp "a list")
- (null null? "the empty list")
- (number number? "a number")
- (simple-array vector? "a simple array")
- (simple-bit-vector vector? "a simple bit vector")
- (simple-string string? "a simple string")
- (simple-vector vector? "a simple vector")
- (standard-char char? "a standard character")
- (string string? "a string")
- (string-char char? "a string character")
- (symbol symbol? "a symbol")
- (vector vector? "a vector")
- ))
-
- (defmacro check-type (place typespec &optional string)
- (let ((typespec-entry (assoc typespec typespec-alist)))
- (unless typespec-entry
- (error "~A is an unrecognized type." typespec))
- (let ((predicate (cadr typespec-entry))
- (description (or string (caddr typespec-entry))))
- (flet ((result (place-var)
- `(unless (,predicate ,place-var)
- (error
- ,(format nil "The value of ~A, ~~S, is not ~A."
- (if (symbolp place) (symbol-name place) "~A")
- (if (stringp description) description "~A"))
- ,@ (if (symbolp place) '() `(',place))
- ,place-var
- ,@ (if (stringp description) '() `(,description))))))
- (if (symbolp place)
- (result place)
- `(let ((temp ,place)) ,(result 'temp)))))))
-
- (defvar *break-on-warnings* nil)
-
- (defun warn (format-string &rest args)
- (format t "~&Warning: ")
- (apply format t format-string args)
- (when *break-on-warnings*
- (bkpt "Warning break" *break-on-warnings*)))